﻿// <copyright file="ContainerAllocationMap.cs" company="Sinclair Community College">
// Copyright 2010 Sinclair Community College
// </copyright>
// <originalAuthor>Patrick S. Seymour (patrick.seymour@gmail.com)</originalAuthor>

namespace SinclairCC.AppV
{
    using System;
    using System.Collections.Generic;
    using System.IO;

    /// <summary>
    /// This container points to the Container Allocation Map structures.
    /// </summary>
    /// <remarks>
    /// This container is a permanent storage for a container bitmap that describes the layout
    /// of the CSR containers on disk.
    /// </remarks>
    public class ContainerAllocationMap : Container
    {
        /* The block count and map length fields could be combined into an AllocMapInfo structure. */

        /// <summary>
        /// The total number of containers allocated for this volume.
        /// </summary>
        private uint blockCount;

        /// <summary>
        /// Currently unused and set to zero (0).
        /// </summary>
        private uint mapLength;

        private StorageBlockInfoTable storageBlockTable;

        /// <summary>
        /// Initializes a new instance of the ContainerAllocationMap class.
        /// </summary>
        public ContainerAllocationMap(System.IO.BinaryReader fileReader)
            : base(fileReader)
        {
            this.storageBlockTable = null;
            this.ReadContainer(fileReader);
        }

        /// <summary>
        /// Reads the actual data for this container from the file.
        /// </summary>
        protected void ReadContainer(System.IO.BinaryReader fileReader)
        {
            this.blockCount = fileReader.ReadUInt32();
            this.mapLength = fileReader.ReadUInt32();
            this.storageBlockTable = new StorageBlockInfoTable(fileReader);
        }

        /// <summary>
        /// Gets the total number of containers allocated for this volume.
        /// </summary>
        public uint BlockCount
        {
            get { return this.blockCount; }
        }

        /// <summary>
        /// This value is currently unused and is always set to zero (0).
        /// </summary>
        public uint MapLength
        {
            get { return this.mapLength; }
            set { this.mapLength = 0; }
        }

        public StorageBlockInfoTable StorageBlockTable
        {
            get { return this.storageBlockTable; }
        }

        public void Process(System.Collections.ObjectModel.Collection<Container> containerTable, uint containerSize, uint storageBlockSize, System.IO.BinaryReader fileReader)
        {
            foreach (StorageBlockInfo sbi in this.storageBlockTable)
            {
                int containersRead = 0;
                int containersProcessed = 0;
                long offset = sbi.GetOffset(containerSize, storageBlockSize);
                if (offset != -1)
                {
                    // Seek to the container's offset.
                    fileReader.BaseStream.Seek(offset, SeekOrigin.Begin);

                    while (containersRead < sbi.BlockCount)
                    {
                        byte[] storageBlockBuffer = new byte[storageBlockSize];
                        int bytesRead = fileReader.Read(storageBlockBuffer, 0, storageBlockBuffer.Length);

                        MemoryStream storageBlockStream = new MemoryStream(storageBlockBuffer, false);
                        System.IO.BinaryReader storageBlockReader = new System.IO.BinaryReader(storageBlockStream);

                        for (long bufferPosition = 0; bufferPosition < storageBlockReader.BaseStream.Length; bufferPosition += containerSize)
                        {
                            byte[] containerBuffer = new byte[containerSize];
                            storageBlockReader.Read(containerBuffer, 0, containerBuffer.Length);
                            containersRead++;
                            MemoryStream containerStream = new MemoryStream(containerBuffer, false);
                            BinaryReader containerReader = new BinaryReader(containerStream);

                            CommonHeader commonHeader = new CommonHeader(containerReader);
                            if (commonHeader.TypeId != 0)
                            {
                                containersProcessed++;
                                /*
                                if (commonHeader.TypeId != ContainerType.CONTAINER_EMPTY)
                                {
                                    Console.Write("Found container of type ");
                                    Console.ForegroundColor = ConsoleColor.Cyan;
                                    Console.Write(commonHeader.TypeId);
                                    Console.ForegroundColor = foreColor;
                                    Console.WriteLine(".");
                                }
                                */
                                containerReader.BaseStream.Seek(0, SeekOrigin.Begin);

                                switch (commonHeader.TypeId)
                                {
                                    case ContainerType.RUN_DIRECTORY:
                                    case ContainerType.PACKAGE_DIRECTORY:
                                        NonRootDirectoryContainer nonRootDir = new NonRootDirectoryContainer(containerReader);
                                        if (nonRootDir.StorageBlockTable != null)
                                        {
                                            nonRootDir.Process(containerSize, storageBlockSize, fileReader);
                                        }
                                        containerTable.Add(nonRootDir);
                                        break;

                                    case ContainerType.CONTAINER_PACKAGE:
                                        PackageContainer contPack = new PackageContainer(containerReader);
                                        /*
                                        contPack.Process(containerSize, storageBlockSize, fileReader);
                                        */
                                        containerTable.Add(contPack);
                                        break;

                                    case ContainerType.RUN_FILE:
                                        RunFileContainer runfileCont = new RunFileContainer(containerReader);
                                        containerTable.Add(runfileCont);
                                        break;
                                    
                                    case ContainerType.PACKAGE_FILE:
                                        PackageFileContainer pkgFileCont = new PackageFileContainer(containerReader);
                                        containerTable.Add(pkgFileCont);
                                        break;

                                    case ContainerType.CONTAINER_EMPTY:
                                        // The container was previously created, but it is no longer needed. In order to reuse it
                                        // in the future, it is marked as empty. It may or may not point to any valid data structure
                                        // and will be reused when needed. This container should be ignored during processing.
                                        break;
                                    
                                    default:
                                        // TODO: Do something with "other" container types.
                                        break;
                                }
                            }
                            containerReader.Close();
                            containerStream.Close();
                            containerStream.Dispose();
                        }
                        storageBlockReader.Close();
                        storageBlockStream.Close();
                        storageBlockStream.Dispose();
                    }
                }
            }
        }
    }
}
